/*
 * Copyright (c) Citrix Systems, Inc.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation, with the additional linking exception as
 * follows:
 * 
 *   Linking this library statically or dynamically with other modules is
 *   making a combined work based on this library. Thus, the terms and
 *   conditions of the GNU General Public License cover the whole combination.
 * 
 *   As a special exception, the copyright holders of this library give you
 *   permission to link this library with independent modules to produce an
 *   executable, regardless of the license terms of these independent modules,
 *   and to copy and distribute the resulting executable under terms of your
 *   choice, provided that you also meet, for each linked independent module,
 *   the terms and conditions of the license of that module. An independent
 *   module is a module which is not derived from or based on this library. If
 *   you modify this library, you may extend this exception to your version of
 *   the library, but you are not obligated to do so. If you do not wish to do
 *   so, delete this exception statement from your version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package com.beyondsphere.xenapi;

import com.beyondsphere.xenapi.Types.BadServerResponse;
import com.beyondsphere.xenapi.Types.XenAPIException;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.apache.xmlrpc.XmlRpcException;

/**
 * A virtual network
 *
 * @author Citrix Systems, Inc.
 */
public class Network extends XenAPIObject {

	/**
	 * The XenAPI reference to this object.
	 */
	protected final String ref;

	/**
	 * For internal use only.
	 */
	Network(String ref) {
		this.ref = ref;
	}

	public String toWireString() {
		return this.ref;
	}

	/**
	 * If obj is a Network, compares XenAPI references for equality.
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj != null && obj instanceof Network) {
			Network other = (Network) obj;
			return other.ref.equals(this.ref);
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() {
		return ref.hashCode();
	}

	/**
	 * Represents all the fields in a Network
	 */
	public static class Record implements Types.Record {
		public String toString() {
			StringWriter writer = new StringWriter();
			PrintWriter print = new PrintWriter(writer);
			print.printf("%1$20s: %2$s\n", "uuid", this.uuid);
			print.printf("%1$20s: %2$s\n", "nameLabel", this.nameLabel);
			print.printf("%1$20s: %2$s\n", "nameDescription",
					this.nameDescription);
			print.printf("%1$20s: %2$s\n", "allowedOperations",
					this.allowedOperations);
			print.printf("%1$20s: %2$s\n", "currentOperations",
					this.currentOperations);
			print.printf("%1$20s: %2$s\n", "VIFs", this.VIFs);
			// print.printf("%1$20s: %2$s\n", "PIFs", this.PIFs);
			print.printf("%1$20s: %2$s\n", "MTU", this.MTU);
			print.printf("%1$20s: %2$s\n", "otherConfig", this.otherConfig);
			print.printf("%1$20s: %2$s\n", "bridge", this.bridge);
			// print.printf("%1$20s: %2$s\n", "blobs", this.blobs);
			print.printf("%1$20s: %2$s\n", "tags", this.tags);
			return writer.toString();
		}

		/**
		 * Convert a network.Record to a Map
		 */
		public Map<String, Object> toMap() {
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("uuid", this.uuid == null ? "" : this.uuid);
			map.put("name_label", this.nameLabel == null ? "" : this.nameLabel);
			map.put("name_description", this.nameDescription == null ? ""
					: this.nameDescription);
			map.put("allowed_operations",
					this.allowedOperations == null ? new LinkedHashSet<Types.NetworkOperations>()
							: this.allowedOperations);
			map.put("current_operations",
					this.currentOperations == null ? new HashMap<String, Types.NetworkOperations>()
							: this.currentOperations);
			map.put("VIFs", this.VIFs == null ? new LinkedHashSet<VIF>()
					: this.VIFs);
			// map.put("PIFs", this.PIFs == null ? new LinkedHashSet<PIF>() :
			// this.PIFs);
			map.put("MTU", this.MTU == null ? 0 : this.MTU);
			map.put("other_config",
					this.otherConfig == null ? new HashMap<String, String>()
							: this.otherConfig);
			map.put("bridge", this.bridge == null ? "" : this.bridge);
			// map.put("blobs", this.blobs == null ? new HashMap<String, Blob>()
			// : this.blobs);
			map.put("tags", this.tags == null ? new LinkedHashSet<String>()
					: this.tags);
			return map;
		}

		/**
		 * Unique identifier/object reference
		 */
		public String uuid;
		/**
		 * a human-readable name
		 */
		public String nameLabel;
		/**
		 * a notes field containg human-readable description
		 */
		public String nameDescription;
		/**
		 * list of the operations allowed in this state. This list is advisory
		 * only and the server state may have changed by the time this field is
		 * read by a client.
		 */
		public Set<Types.NetworkOperations> allowedOperations;
		/**
		 * links each of the running tasks using this object (by reference) to a
		 * current_operation enum which describes the nature of the task.
		 */
		public Map<String, Types.NetworkOperations> currentOperations;
		/**
		 * list of connected vifs
		 */
		public Set<VIF> VIFs;
		// /**
		// * list of connected pifs
		// */
		// public Set<PIF> PIFs;
		/**
		 * MTU in octets
		 */
		public Long MTU;
		/**
		 * additional configuration
		 */
		public Map<String, String> otherConfig;
		/**
		 * name of the bridge corresponding to this network on the local host
		 */
		public String bridge;
		// /**
		// * Binary blobs associated with this network
		// */
		// public Map<String, Blob> blobs;
		/**
		 * user-specified tags for categorization purposes
		 */
		public Set<String> tags;
	}

	/**
	 * Get a record containing the current state of the given network.
	 *
	 * @return all fields from the object
	 */
	public Network.Record getRecord(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_record";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toNetworkRecord(result);
	}

	/**
	 * Get a reference to the network instance with the specified UUID.
	 *
	 * @param uuid
	 *            UUID of object to return
	 * @return reference to the object
	 */
	public static Network getByUuid(Connection c, String uuid)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.get_by_uuid";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(uuid) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toNetwork(result);
	}

	// /**
	// * Create a new network instance, and return its handle.
	// *
	// * @param record All constructor arguments
	// * @return Task
	// */
	// public static Task createAsync(Connection c, Network.Record record)
	// throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "Async.network.create";
	// String session = c.getSessionReference();
	// Map<String, Object> record_map = record.toMap();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(record_map)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toTask(result);
	// }
	//
	// /**
	// * Create a new network instance, and return its handle.
	// *
	// * @param record All constructor arguments
	// * @return reference to the newly created object
	// */
	// public static Network create(Connection c, Network.Record record) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.create";
	// String session = c.getSessionReference();
	// Map<String, Object> record_map = record.toMap();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(record_map)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toNetwork(result);
	// }
	//
	// /**
	// * Destroy the specified network instance.
	// *
	// * @return Task
	// */
	// public Task destroyAsync(Connection c) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "Async.network.destroy";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toTask(result);
	// }
	//
	// /**
	// * Destroy the specified network instance.
	// *
	// */
	// public void destroy(Connection c) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.destroy";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref)};
	// Map response = c.dispatch(method_call, method_params);
	// return;
	// }

	/**
	 * Get all the network instances with the given label.
	 *
	 * @param label
	 *            label of object to return
	 * @return references to objects with matching names
	 */
	public static Set<Network> getByNameLabel(Connection c, String label)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.get_by_name_label";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(label) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toSetOfNetwork(result);
	}

	/**
	 * Get the uuid field of the given network.
	 *
	 * @return value of the field
	 */
	public String getUuid(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_uuid";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toString(result);
	}

	/**
	 * Get the name/label field of the given network.
	 *
	 * @return value of the field
	 */
	public String getNameLabel(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_name_label";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toString(result);
	}

	/**
	 * Get the name/description field of the given network.
	 *
	 * @return value of the field
	 */
	public String getNameDescription(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_name_description";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toString(result);
	}

	/**
	 * Get the allowed_operations field of the given network.
	 *
	 * @return value of the field
	 */
	public Set<Types.NetworkOperations> getAllowedOperations(Connection c)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.get_allowed_operations";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toSetOfNetworkOperations(result);
	}

	/**
	 * Get the current_operations field of the given network.
	 *
	 * @return value of the field
	 */
	public Map<String, Types.NetworkOperations> getCurrentOperations(
			Connection c) throws BadServerResponse, XenAPIException,
			XmlRpcException {
		String method_call = "network.get_current_operations";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toMapOfStringNetworkOperations(result);
	}

	/**
	 * Get the VIFs field of the given network.
	 *
	 * @return value of the field
	 */
	public Set<VIF> getVIFs(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_VIFs";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toSetOfVIF(result);
	}

	// /**
	// * Get the PIFs field of the given network.
	// *
	// * @return value of the field
	// */
	// public Set<PIF> getPIFs(Connection c) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.get_PIFs";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toSetOfPIF(result);
	// }

	/**
	 * Get the MTU field of the given network.
	 *
	 * @return value of the field
	 */
	public Long getMTU(Connection c) throws BadServerResponse, XenAPIException,
			XmlRpcException {
		String method_call = "network.get_MTU";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toLong(result);
	}

	/**
	 * Get the other_config field of the given network.
	 *
	 * @return value of the field
	 */
	public Map<String, String> getOtherConfig(Connection c)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.get_other_config";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toMapOfStringString(result);
	}

	/**
	 * Get the bridge field of the given network.
	 *
	 * @return value of the field
	 */
	public String getBridge(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_bridge";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toString(result);
	}

	// /**
	// * Get the blobs field of the given network.
	// *
	// * @return value of the field
	// */
	// public Map<String, Blob> getBlobs(Connection c) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.get_blobs";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toMapOfStringBlob(result);
	// }

	// /**
	// * Get the tags field of the given network.
	// *
	// * @return value of the field
	// */
	// public Set<String> getTags(Connection c) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.get_tags";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toSetOfString(result);
	// }

	/**
	 * Set the name/label field of the given network.
	 *
	 * @param label
	 *            New value to set
	 */
	public void setNameLabel(Connection c, String label)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.set_name_label";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(label) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	/**
	 * Set the name/description field of the given network.
	 *
	 * @param description
	 *            New value to set
	 */
	public void setNameDescription(Connection c, String description)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.set_name_description";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref),
				Marshalling.toXMLRPC(description) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	/**
	 * Set the MTU field of the given network.
	 *
	 * @param MTU
	 *            New value to set
	 */
	public void setMTU(Connection c, Long MTU) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.set_MTU";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(MTU) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	/**
	 * Set the other_config field of the given network.
	 *
	 * @param otherConfig
	 *            New value to set
	 */
	public void setOtherConfig(Connection c, Map<String, String> otherConfig)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.set_other_config";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref),
				Marshalling.toXMLRPC(otherConfig) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	/**
	 * Add the given key-value pair to the other_config field of the given
	 * network.
	 *
	 * @param key
	 *            Key to add
	 * @param value
	 *            Value to add
	 */
	public void addToOtherConfig(Connection c, String key, String value)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.add_to_other_config";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(key),
				Marshalling.toXMLRPC(value) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	/**
	 * Remove the given key and its corresponding value from the other_config
	 * field of the given network. If the key is not in that Map, then do
	 * nothing.
	 *
	 * @param key
	 *            Key to remove
	 */
	public void removeFromOtherConfig(Connection c, String key)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.remove_from_other_config";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(key) };
		Map response = c.dispatch(method_call, method_params);
		return;
	}

	// /**
	// * Set the tags field of the given network.
	// *
	// * @param tags New value to set
	// */
	// public void setTags(Connection c, Set<String> tags) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.set_tags";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(tags)};
	// Map response = c.dispatch(method_call, method_params);
	// return;
	// }
	//
	// /**
	// * Add the given value to the tags field of the given network. If the
	// value is already in that Set, then do nothing.
	// *
	// * @param value New value to add
	// */
	// public void addTags(Connection c, String value) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.add_tags";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(value)};
	// Map response = c.dispatch(method_call, method_params);
	// return;
	// }
	//
	// /**
	// * Remove the given value from the tags field of the given network. If the
	// value is not in that Set, then do nothing.
	// *
	// * @param value Value to remove
	// */
	// public void removeTags(Connection c, String value) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.remove_tags";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(value)};
	// Map response = c.dispatch(method_call, method_params);
	// return;
	// }
	//
	// /**
	// * Create a placeholder for a named binary blob of data that is associated
	// with this pool
	// *
	// * @param name The name associated with the blob
	// * @param mimeType The mime type for the data. Empty string translates to
	// application/octet-stream
	// * @return Task
	// */
	// public Task createNewBlobAsync(Connection c, String name, String
	// mimeType) throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "Async.network.create_new_blob";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(name),
	// Marshalling.toXMLRPC(mimeType)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toTask(result);
	// }
	//
	// /**
	// * Create a placeholder for a named binary blob of data that is associated
	// with this pool
	// *
	// * @param name The name associated with the blob
	// * @param mimeType The mime type for the data. Empty string translates to
	// application/octet-stream
	// * @return The reference of the blob, needed for populating its data
	// */
	// public Blob createNewBlob(Connection c, String name, String mimeType)
	// throws
	// BadServerResponse,
	// XenAPIException,
	// XmlRpcException {
	// String method_call = "network.create_new_blob";
	// String session = c.getSessionReference();
	// Object[] method_params = {Marshalling.toXMLRPC(session),
	// Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(name),
	// Marshalling.toXMLRPC(mimeType)};
	// Map response = c.dispatch(method_call, method_params);
	// Object result = response.get("Value");
	// return Types.toBlob(result);
	// }

	/**
	 * Return a list of all the networks known to the system.
	 *
	 * @return references to all objects
	 */
	public static Set<Network> getAll(Connection c) throws BadServerResponse,
			XenAPIException, XmlRpcException {
		String method_call = "network.get_all";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toSetOfNetwork(result);
	}

	/**
	 * Return a map of network references to network records for all networks
	 * known to the system.
	 *
	 * @return records of all objects
	 */
	public static Map<Network, Network.Record> getAllRecords(Connection c, Host host)
			throws BadServerResponse, XenAPIException, XmlRpcException {
		String method_call = "network.get_all_records_by_host";
		String session = c.getSessionReference();
		Object[] method_params = { Marshalling.toXMLRPC(session),
				Marshalling.toXMLRPC(host) };
		Map response = c.dispatch(method_call, method_params);
		Object result = response.get("Value");
		return Types.toMapOfNetworkNetworkRecord(result);
	}

}